home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 22 / Cream of the Crop 22.iso / comm / rzsz0916.zip / RZ.C < prev    next >
C/C++ Source or Header  |  1996-09-16  |  30KB  |  1,342 lines

  1. #define VERSION "3.44 09-16-96"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*
  5.  *
  6.  * rz.c By Chuck Forsberg
  7.  *    Copyright 1996 Omen Technology Inc All Rights Reserved
  8.  *
  9.  * A program for Unix to receive files and commands from computers running
  10.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
  11.  *  rz uses Unix buffered input to reduce wasted CPU time.
  12.  *
  13.  * 
  14.  *********************************************************************
  15.  *********************************************************************
  16.  * 
  17.  *
  18.  *    This version implements numerous enhancements including ZMODEM
  19.  *    Run Length Encoding and variable length headers.  These
  20.  *    features were not funded by the original Telenet development
  21.  *    contract.
  22.  * 
  23.  * 
  24.  * This software may be freely used for educational (didactic
  25.  * only) purposes.  "Didactic" means it is used as a study item
  26.  * in a course teaching the workings of computer protocols.
  27.  * 
  28.  * This software may also be freely used to support file transfer
  29.  * operations to or from duly licensed Omen Technology products.
  30.  * This includes DSZ, GSZ, ZCOMM, Professional-YAM and PowerCom.
  31.  * Institutions desiring to use rz/sz this way should add the
  32.  * following to the sz compile line:    -DCOMPL
  33.  * Programs based on stolen or public domain ZMODEM materials are
  34.  * not included.  Use with other commercial or shareware programs
  35.  * (Crosstalk, Procomm, etc.) REQUIRES REGISTRATION.
  36.  * 
  37.  *
  38.  *  Any programs which incorporate part or all of this code must be
  39.  *  provided in source form with this notice intact except by
  40.  *  prior written permission from Omen Technology Incorporated.
  41.  *  This includes compiled executables of this program.
  42.  *
  43.  *   The .doc files and the file "mailer.rz" must also be included.
  44.  * 
  45.  * Use of this software for commercial or administrative purposes
  46.  * except when exclusively limited to interfacing Omen Technology
  47.  * products requires license payment of $20.00 US per user
  48.  * (less in quantity, see mailer.rz).  Use of this code by
  49.  * inclusion, decompilation, reverse engineering or any other means
  50.  * constitutes agreement to these conditions and acceptance of
  51.  * liability to license the materials and payment of reasonable
  52.  * legal costs necessary to enforce this license agreement.
  53.  *
  54.  *
  55.  *        Omen Technology Inc
  56.  *        Post Office Box 4681
  57.  *        Portland OR 97208
  58.  *
  59.  *    This code is made available in the hope it will be useful,
  60.  *    BUT WITHOUT ANY WARRANTY OF ANY KIND OR LIABILITY FOR ANY
  61.  *    DAMAGES OF ANY KIND.
  62.  *
  63.  *
  64.  *  -DMD may be added to compiler command line to compile in
  65.  *    Directory-creating routines from Public Domain TAR by John Gilmore
  66.  *
  67.  *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
  68.  */
  69.  
  70. char *Copyrrz = "Copyright 1996 Omen Technology Inc All Rights Reserved";
  71.  
  72.  
  73. #define LOGFILE "/tmp/rzlog"
  74. #define LOGFILE2 "rzlog"
  75. #include <stdio.h>
  76. #include <signal.h>
  77. #include <ctype.h>
  78. #include <errno.h>
  79. extern int errno;
  80.  
  81. #define OK 0
  82. #define FALSE 0
  83. #define TRUE 1
  84. #define ERROR (-1)
  85.  
  86. /*
  87.  * Max value for HOWMANY is 255.
  88.  *   A larger value reduces system overhead but may evoke kernel bugs.
  89.  */
  90. #ifndef HOWMANY
  91. #define HOWMANY 96
  92. #endif
  93.  
  94. /* Ward Christensen / CP/M parameters - Don't change these! */
  95. #define ENQ 005
  96. #define CAN ('X'&037)
  97. #define XOFF ('s'&037)
  98. #define XON ('q'&037)
  99. #define SOH 1
  100. #define STX 2
  101. #define EOT 4
  102. #define ACK 6
  103. #define NAK 025
  104. #define CPMEOF 032
  105. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  106. #define TIMEOUT (-2)
  107. #define RCDO (-3)
  108. #define GCOUNT (-4)
  109. #define ERRORMAX 5
  110. #define RETRYMAX 5
  111. #define WCEOT (-10)
  112. #define PATHLEN 257    /* ready for 4.2 bsd ? */
  113. #define UNIXFILE 0xF000    /* The S_IFMT file mask bit for stat */
  114.  
  115. int Zmodem=0;        /* ZMODEM protocol requested */
  116. int Nozmodem = 0;    /* If invoked as "rb" */
  117. unsigned Baudrate = 9600;
  118.  
  119.  
  120. #include "rbsb.c"    /* most of the system dependent stuff here */
  121. #include "crctab.c"
  122. char endmsg[90] = {0};    /* Possible message to display on exit */
  123. char Zsendmask[33];    /* Additional control chars to mask */
  124.  
  125. char *substr();
  126. FILE *fout;
  127.  
  128. /*
  129.  * Routine to calculate the free bytes on the current file system
  130.  *  ~0 means many free bytes (unknown)
  131.  */
  132. long getfree()
  133. {
  134.     return(2147483647);    /* many free bytes ... */
  135. }
  136.  
  137. int Lastrx;
  138. long rxbytes;
  139. int Crcflg;
  140. int Firstsec;
  141. int Eofseen;        /* indicates cpm eof (^Z) has been received */
  142. int errors;
  143. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  144.  
  145. #define DEFBYTL 2000000000L    /* default rx file size */
  146. long Bytesleft;    /* number of bytes of incoming file left */
  147. long Modtime;        /* Unix style mod time for incoming file */
  148. int Filemode;        /* Unix style mode for incoming file */
  149. long Totalleft;
  150. long Filesleft;
  151. char Pathname[PATHLEN];
  152. char *Progname;        /* the name by which we were called */
  153.  
  154. int Batch=0;
  155. int Thisbinary;        /* current file is to be received in bin mode */
  156. int Rxbinary=FALSE;    /* receive all files in bin mode */
  157. int Rxascii=FALSE;    /* receive files in ascii (translate) mode */
  158. int Blklen;        /* record length of received packets */
  159.  
  160. #ifdef SEGMENTS
  161. int chinseg = 0;    /* Number of characters received in this data seg */
  162. char secbuf[1+(SEGMENTS+1)*1024];
  163. #else
  164. char secbuf[1025];
  165. #endif
  166.  
  167.  
  168. time_t timep[2];
  169. char Lzmanag;        /* Local file management request */
  170. char Lzconv;        /* Local ZMODEM file conversion request */
  171. char zconv;        /* ZMODEM file conversion request */
  172. char zmanag;        /* ZMODEM file management request */
  173. char ztrans;        /* ZMODEM file transport request */
  174. int Zctlesc;        /* Encode control characters */
  175. int Zrwindow = 1400;    /* RX window size (controls garbage count) */
  176.  
  177. /*
  178.  * Log an error
  179.  */
  180. /*VARARGS1*/
  181. void
  182. zperr(s,p,u)
  183. char *s, *p, *u;
  184. {
  185.     if (Verbose <= 0)
  186.         return;
  187.     fprintf(stderr, "Retry %d: ", errors);
  188.     fprintf(stderr, s, p, u);
  189.     fprintf(stderr, "\n");
  190. }
  191.  
  192. #include "zm.c"
  193. #include "zmr.c"
  194.  
  195. int tryzhdrtype=ZRINIT;    /* Header type to send corresponding to Last rx close */
  196.  
  197. /* called by signal interrupt or terminate to clean things up */
  198. void
  199. bibi(n)
  200. {
  201.     if (Zmodem)
  202.         zmputs(Attn);
  203.     canit(); mode(0);
  204.     fprintf(stderr, "rz: caught signal %d; exiting", n);
  205.     exit(3);
  206. }
  207.  
  208. main(argc, argv)
  209. char *argv[];
  210. {
  211.     register char *cp;
  212.     register npats;
  213.     char *virgin, **patts;
  214.     int exitcode = 0;
  215.  
  216.     Rxtimeout = 100;
  217.     setbuf(stderr, NULL);
  218.     if (((cp = getenv("RESTRICTED")) != 0)  &&  *cp == '1')
  219.         Restricted=TRUE;
  220.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  221.         Restricted=TRUE;
  222.  
  223.     chkinvok(virgin=argv[0]);
  224.     inittty();
  225.     npats = 0;
  226.     while (--argc) {
  227.         cp = *++argv;
  228.         if (*cp == '-') {
  229.             ++cp;
  230.             while( *cp) {
  231.                 if (isdigit(*cp)) {
  232.                     ++cp;  continue;
  233.                 }
  234.                 switch(*cp++) {
  235.                 case '\\':
  236.                      *cp = toupper(*cp);  continue;
  237.                 case 'a':
  238.                     if (!Batch || Nozmodem)
  239.                         Rxascii=TRUE;
  240.                     else
  241.                         usage();
  242.                     break;
  243.                 case 't':
  244.                     if (isdigit(*cp))
  245.                         Rxtimeout = atoi(cp);
  246.                     else {
  247.                         if (--argc < 1)
  248.                             usage();
  249.                         Rxtimeout = atoi(*++argv);
  250.                     }
  251.                     if (Rxtimeout<1 || Rxtimeout>1000)
  252.                         usage();
  253.                     break;
  254.                 case 'w':
  255.                     if (isdigit(*cp))
  256.                         Zrwindow = atoi(cp);
  257.                     else {
  258.                         if (--argc < 1)
  259.                             usage();
  260.                         Zrwindow = atoi(*++argv);
  261.                     }
  262.                     break;
  263.                 case 'v':
  264.                     ++Verbose; break;
  265.                 case 'y':
  266.                     Lzmanag = ZMCLOB;
  267.                     break;
  268.                 default:
  269.                     usage();
  270.                 }
  271.             }
  272.         }
  273.         else if ( !npats && argc>0) {
  274.             if (argv[0][0]) {
  275.                 npats=argc;
  276.                 patts=argv;
  277.             }
  278.         }
  279.     }
  280.     if (npats > 1)
  281.         usage();
  282.     if (Batch && npats)
  283.         usage();
  284.     if (Verbose) {
  285.         if (freopen(LOGFILE, "a", stderr)==NULL)
  286.             if (freopen(LOGFILE2, "a", stderr)==NULL) {
  287.                 fprintf(stderr, "Can't open log file!\n");
  288.                 exit(2);
  289.             }
  290.         setbuf(stderr, NULL);
  291.         fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
  292.     }
  293.     vfile("%s %s for %s tty=%s\n", Progname, VERSION, OS, Nametty);
  294.     mode(1);
  295.     if (signal(SIGINT, bibi) == SIG_IGN) {
  296.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  297.     }
  298.     else {
  299.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  300.     }
  301.     signal(SIGTERM, bibi);
  302.     if (wcreceive(npats, patts)==ERROR) {
  303.         exitcode=1;
  304.         canit();
  305.     }
  306.     if (exitcode && !Zmodem)    /* bellow again with all thy might. */
  307.         canit();
  308.     if (endmsg[0])
  309.         fprintf(stderr, "  %s: %s\r\n", Progname, endmsg);
  310.     fprintf(stderr, "%s %s finished.\r\n", Progname, VERSION);
  311.     fflush(stderr);
  312. #ifndef REGISTERED
  313.     /* Removing or disabling this code without registering is theft */
  314.     if (!Usevhdrs) {
  315.         fprintf(stderr, "\n\n\n**** UNREGISTERED COPY *****\r\n");
  316.         fprintf(stderr, "Please read the License Agreement in rz.doc\r\n");
  317.         fflush(stderr);
  318.         sleep(10);
  319.     }
  320. #endif
  321.     mode(0);
  322.     if(exitcode)
  323.         exit(1);
  324.     exit(0);
  325.     /* NOTREACHED */
  326. }
  327.  
  328.  
  329. usage()
  330. {
  331.     fprintf(stderr,
  332.     "Receive Files and Commands with ZMODEM/YMODEM/XMODEM Protocol\n\n");
  333.     fprintf(stderr,"Usage:    rz [-v]   [-wN] [-tT]    (ZMODEM)\n");
  334.     fprintf(stderr,"or    rb [-avy] [-tT]        (YMODEM)\n");
  335.     fprintf(stderr,"or    rc [-avy] [-tT] file    (XMODEM-CRC)\n");
  336.     fprintf(stderr,"or    rx [-avy] [-tT] file    (XMODEM)\n\n");
  337.     fprintf(stderr,
  338. "Supports the following incoming ZMODEM options given to the sending program:\n\
  339.     compression (-Z), binary (-b), ASCII CR/LF>NL (-a), newer(-n),\n\
  340.     newer+longer(-N), protect (-p), Crash Recovery (-r),\n\
  341.     clobber (-y), match+clobber (-Y),  and append (-+).\n\n");
  342.     fprintf(stderr,"%s %s for %s by Chuck Forsberg, Omen Technology INC\n",
  343.       Progname, VERSION, OS);
  344.     fprintf(stderr, "\t\t\042The High Reliability Software\042\n\n");
  345.     fprintf(stderr,"Copyright (c) 1996 Omen Technology INC All Rights Reserved\n");
  346.     fprintf(stderr,
  347.     "See rz.doc for option descriptions and licensing information.\n\n");
  348.     fprintf(stderr,
  349.     "This program is designed to talk to terminal programs,\nnot to be called by one.\n");
  350.     fprintf(stderr,
  351.     "\nTechnical support hotline: 900-555-7836 (1-900-555-RTFM) $4.69/min.\n\n");
  352. #ifndef REGISTERED
  353.     fprintf(stderr, "\n\n\n      **** UNREGISTERED COPY *****\r\n");
  354.     fprintf(stderr, "Please read the License Agreement in rz.doc\r\n");
  355. #endif
  356.     exit(2);
  357. }
  358.  
  359. /*
  360.  * Let's receive something already.
  361.  */
  362.  
  363. char *rbmsg = "%s ready. Type \"%s file ...\" to your modem program\n\r";
  364.  
  365. wcreceive(argc, argp)
  366. char **argp;
  367. {
  368.     register c;
  369.  
  370.     if (Batch || argc==0) {
  371.         Crcflg=1;
  372.         fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz");
  373.         if (c=tryz()) {
  374.             if (c == ZCOMPL)
  375.                 return OK;
  376.             if (c == ERROR)
  377.                 goto fubar;
  378.             c = rzfiles();
  379.             if (c)
  380.                 goto fubar;
  381.         } else {
  382.             for (;;) {
  383.                 if (wcrxpn(secbuf)== ERROR)
  384.                     goto fubar;
  385.                 if (secbuf[0]==0)
  386.                     return OK;
  387.                 if (procheader(secbuf))
  388.                     goto fubar;
  389.                 if (wcrx()==ERROR)
  390.                     goto fubar;
  391.             }
  392.         }
  393.     } else {
  394.         Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  395.  
  396.         procheader(""); strcpy(Pathname, *argp); checkpath(Pathname);
  397.         fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
  398.         if ((fout=fopen(Pathname, "w")) == NULL)
  399.             return ERROR;
  400.         if (wcrx()==ERROR)
  401.             goto fubar;
  402.     }
  403.     return OK;
  404. fubar:
  405.     canit();
  406.     Modtime = 1;
  407.     if (fout)
  408.         fclose(fout);
  409.     if (Restricted) {
  410.         unlink(Pathname);
  411.         fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
  412.     }
  413.     return ERROR;
  414. }
  415.  
  416.  
  417. /*
  418.  * Fetch a pathname from the other end as a C ctyle ASCIZ string.
  419.  * Length is indeterminate as long as less than Blklen
  420.  * A null string represents no more files (YMODEM)
  421.  */
  422. wcrxpn(rpn)
  423. char *rpn;    /* receive a pathname */
  424. {
  425.     register c;
  426.  
  427.     purgeline();
  428.  
  429. et_tu:
  430.     Firstsec=TRUE;  Eofseen=FALSE;
  431.     sendline(Crcflg?WANTCRC:NAK);  flushmo();
  432.     Lleft=0;    /* Do read next time ... */
  433.     switch (c = wcgetsec(rpn, 100)) {
  434.     case WCEOT:
  435.         zperr( "Pathname fetch returned %d", c);
  436.         sendline(ACK);  flushmo();
  437.         Lleft=0;    /* Do read next time ... */
  438.         readline(1);
  439.         goto et_tu;
  440.     case 0:
  441.         sendline(ACK);  flushmo(); return OK;
  442.     default:
  443.         return ERROR;
  444.     }
  445. }
  446.  
  447. /*
  448.  * Adapted from CMODEM13.C, written by
  449.  * Jack M. Wierda and Roderick W. Hart
  450.  */
  451.  
  452. wcrx()
  453. {
  454.     register int sectnum, sectcurr;
  455.     register char sendchar;
  456.     int cblklen;            /* bytes to dump this block */
  457.  
  458.     Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
  459.     sendchar=Crcflg?WANTCRC:NAK;
  460.  
  461.     for (;;) {
  462.         sendline(sendchar);    /* send it now, we're ready! */
  463.         flushmo();
  464.         Lleft=0;    /* Do read next time ... */
  465.         sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
  466.         if (sectcurr==(sectnum+1 &0377)) {
  467.             sectnum++;
  468.             cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
  469.             if (putsec(secbuf, cblklen)==ERROR)
  470.                 return ERROR;
  471.             if ((Bytesleft-=cblklen) < 0)
  472.                 Bytesleft = 0;
  473.             sendchar=ACK;
  474.         }
  475.         else if (sectcurr==(sectnum&0377)) {
  476.             zperr( "Received dup Sector");
  477.             sendchar=ACK;
  478.         }
  479.         else if (sectcurr==WCEOT) {
  480.             if (closeit())
  481.                 return ERROR;
  482.             sendline(ACK); flushmo();
  483.             Lleft=0;    /* Do read next time ... */
  484.             return OK;
  485.         }
  486.         else if (sectcurr==ERROR)
  487.             return ERROR;
  488.         else {
  489.             zperr( "Sync Error");
  490.             return ERROR;
  491.         }
  492.     }
  493.     /* NOTREACHED */
  494. }
  495.  
  496. /*
  497.  * Wcgetsec fetches a Ward Christensen type sector.
  498.  * Returns sector number encountered or ERROR if valid sector not received,
  499.  * or CAN CAN received
  500.  * or WCEOT if eot sector
  501.  * time is timeout for first char, set to 4 seconds thereafter
  502.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  503.  *    (Caller must do that when he is good and ready to get next sector)
  504.  */
  505.  
  506. wcgetsec(rxbuf, maxtime)
  507. char *rxbuf;
  508. int maxtime;
  509. {
  510.     register checksum, wcj, firstch;
  511.     register unsigned short oldcrc;
  512.     register char *p;
  513.     int sectcurr;
  514.  
  515.     for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
  516.  
  517.         if ((firstch=readline(maxtime))==STX) {
  518.             Blklen=1024; goto get2;
  519.         }
  520.         if (firstch==SOH) {
  521.             Blklen=128;
  522. get2:
  523.             sectcurr=readline(1);
  524.             if ((sectcurr+(oldcrc=readline(1)))==0377) {
  525.                 oldcrc=checksum=0;
  526.                 for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
  527.                     if ((firstch=readline(1)) < 0)
  528.                         goto bilge;
  529.                     oldcrc=updcrc(firstch, oldcrc);
  530.                     checksum += (*p++ = firstch);
  531.                 }
  532.                 if ((firstch=readline(1)) < 0)
  533.                     goto bilge;
  534.                 if (Crcflg) {
  535.                     oldcrc=updcrc(firstch, oldcrc);
  536.                     if ((firstch=readline(1)) < 0)
  537.                         goto bilge;
  538.                     oldcrc=updcrc(firstch, oldcrc);
  539.                     if (oldcrc & 0xFFFF)
  540.                         zperr( "CRC");
  541.                     else {
  542.                         Firstsec=FALSE;
  543.                         return sectcurr;
  544.                     }
  545.                 }
  546.                 else if (((checksum-firstch)&0377)==0) {
  547.                     Firstsec=FALSE;
  548.                     return sectcurr;
  549.                 }
  550.                 else
  551.                     zperr( "Checksum");
  552.             }
  553.             else
  554.                 zperr("Sector number garbled");
  555.         }
  556.         /* make sure eot really is eot and not just mixmash */
  557.         else if (firstch==EOT && Lleft==0)
  558.             return WCEOT;
  559.         else if (firstch==CAN) {
  560.             if (Lastrx==CAN) {
  561.                 zperr( "Sender CANcelled");
  562.                 return ERROR;
  563.             } else {
  564.                 Lastrx=CAN;
  565.                 continue;
  566.             }
  567.         }
  568.         else if (firstch==TIMEOUT) {
  569.             if (Firstsec)
  570.                 goto humbug;
  571. bilge:
  572.             zperr( "TIMEOUT");
  573.         }
  574.         else
  575.             zperr( "Got 0%o sector header", firstch);
  576.  
  577. humbug:
  578.         Lastrx=0;
  579.         while(readline(1)!=TIMEOUT)
  580.             ;
  581.         if (Firstsec) {
  582.             sendline(Crcflg?WANTCRC:NAK);  flushmo();
  583.             Lleft=0;    /* Do read next time ... */
  584.         } else {
  585.             maxtime=40; sendline(NAK);  flushmo();
  586.             Lleft=0;    /* Do read next time ... */
  587.         }
  588.     }
  589.     /* try to stop the bubble machine. */
  590.     canit();
  591.     return ERROR;
  592. }
  593.  
  594.  
  595. /*
  596.  * Process incoming file information header
  597.  *  Returns 0 for success, other codes for errors
  598.  *  or skip conditions.
  599.  */
  600. procheader(name)
  601. char *name;
  602. {
  603.     register char *openmode, *p;
  604.     static dummy;
  605.     struct stat f;
  606.  
  607.     /* set default parameters and overrides */
  608.     openmode = "w";
  609.     Thisbinary = (!Rxascii) || Rxbinary;
  610.     if (zconv == ZCBIN && Lzconv != ZCRESUM)
  611.         Lzconv = zconv;            /* Remote Binary override */
  612.     if (Lzconv)
  613.         zconv = Lzconv;
  614.     if (Lzmanag)
  615.         zmanag = Lzmanag;
  616.  
  617.     /*
  618.      *  Process ZMODEM remote file management requests
  619.      */
  620.     if (!Rxbinary && zconv == ZCNL)    /* Remote ASCII override */
  621.         Thisbinary = 0;
  622.     if (zconv == ZCBIN)    /* Remote Binary override */
  623.         Thisbinary = TRUE;
  624.     else if (zmanag == ZMAPND)
  625.         openmode = "a";
  626.  
  627.     Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  628.  
  629.     if (!name || !*name)
  630.         return 0;
  631.  
  632.     p = name + 1 + strlen(name);
  633.     if (*p) {    /* file coming from Unix or DOS system */
  634.         sscanf(p, "%ld%lo%o%lo%d%ld%d%d",
  635.           &Bytesleft, &Modtime, &Filemode,
  636.           &dummy, &Filesleft, &Totalleft, &dummy, &dummy);
  637.         if (Filemode & UNIXFILE)
  638.             ++Thisbinary;
  639.         if (Verbose) {
  640.             fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
  641.               name, Bytesleft, Modtime, Filemode);
  642.             fprintf(stderr,  "YMODEM header: %s\n", p);
  643.         }
  644.     }
  645.  
  646.  
  647.     else {        /* File coming from CP/M system */
  648.         for (p=name; *p; ++p)        /* change / to _ */
  649.             if ( *p == '/')
  650.                 *p = '_';
  651.  
  652.         if ( *--p == '.')        /* zap trailing period */
  653.             *p = 0;
  654.     }
  655.  
  656.     strcpy(Pathname, name);
  657.     checkpath(name);
  658.  
  659.     if (*name && stat(name, &f)!= -1) {
  660.         zmanag &= ZMMASK;
  661.         vfile("Current %s is %ld %lo", name, f.st_size, f.st_mtime);
  662.         if (Thisbinary && zconv==ZCRESUM) {
  663.             rxbytes = f.st_size & ~511;
  664.             if (Bytesleft < rxbytes) {
  665.                 rxbytes = 0;  goto doopen;
  666.             } else
  667.                 openit(name, "r+");
  668.             if ( !fout)
  669.                 return ZFERR;
  670.             if (fseek(fout, rxbytes, 0)) {
  671.                 closeit();
  672.                 return ZFERR;
  673.             }
  674.             vfile("Crash recovery at %ld", rxbytes);
  675.             return 0;
  676.         }
  677.         else if ((zmanag==ZMNEW) ||
  678.           ((zmanag==ZMNEWL) && Bytesleft <= f.st_size) ) {
  679.             if ((f.st_mtime+1) >= Modtime)
  680.                 goto skipfile;
  681.             goto doopen;
  682.         }
  683.         switch (zmanag & ZMMASK) {
  684.         case ZMCLOB:
  685.         case ZMAPND:
  686.             goto doopen;
  687.         default:
  688.             goto skipfile;
  689.         }
  690.     } else if (zmanag & ZMSKNOLOC) {
  691. skipfile:
  692.         vfile("Skipping %s", name);
  693.         return ZSKIP;
  694.     }
  695. doopen:
  696.     openit(name, openmode);
  697. #ifdef MD
  698.     if ( !fout)
  699.         if (make_dirs(name))
  700.             openit(name, openmode);
  701. #endif
  702.     if ( !fout)
  703.         return ZFERR;
  704.     return 0;
  705. }
  706.  
  707. openit(name, openmode)
  708. char *name, *openmode;
  709. {
  710.     if (strcmp(name, "-"))
  711.         fout = fopen(name, openmode);
  712.     else if (isatty(1))
  713.         fout = fopen("stdout", "a");
  714.     else
  715.         fout = stdout;
  716. }
  717.  
  718. #ifdef MD
  719. /*
  720.  *  Directory-creating routines from Public Domain TAR by John Gilmore
  721.  */
  722.  
  723. /*
  724.  * After a file/link/symlink/dir creation has failed, see if
  725.  * it's because some required directory was not present, and if
  726.  * so, create all required dirs.
  727.  */
  728. make_dirs(pathname)
  729. register char *pathname;
  730. {
  731.     register char *p;        /* Points into path */
  732.     int madeone = 0;        /* Did we do anything yet? */
  733.     int save_errno = errno;        /* Remember caller's errno */
  734.  
  735.     if (errno != ENOENT)
  736.         return 0;        /* Not our problem */
  737.  
  738.     for (p = strchr(pathname, '/'); p != NULL; p = strchr(p+1, '/')) {
  739.         /* Avoid mkdir of empty string, if leading or double '/' */
  740.         if (p == pathname || p[-1] == '/')
  741.             continue;
  742.         /* Avoid mkdir where last part of path is '.' */
  743.         if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/'))
  744.             continue;
  745.         *p = 0;                /* Truncate the path there */
  746.         if ( !mkdir(pathname, 0777)) {    /* Try to create it as a dir */
  747.             vfile("Made directory %s\n", pathname);
  748.             madeone++;        /* Remember if we made one */
  749.             *p = '/';
  750.             continue;
  751.         }
  752.         *p = '/';
  753.         if (errno == EEXIST)        /* Directory already exists */
  754.             continue;
  755.         /*
  756.          * Some other error in the mkdir.  We return to the caller.
  757.          */
  758.         break;
  759.     }
  760.     errno = save_errno;        /* Restore caller's errno */
  761.     return madeone;            /* Tell them to retry if we made one */
  762. }
  763.  
  764. #if (MD != 2)
  765. #define    TERM_SIGNAL(status)    ((status) & 0x7F)
  766. #define TERM_COREDUMP(status)    (((status) & 0x80) != 0)
  767. #define TERM_VALUE(status)    ((status) >> 8)
  768. /*
  769.  * Make a directory.  Compatible with the mkdir() system call on 4.2BSD.
  770.  */
  771. mkdir(dpath, dmode)
  772. char *dpath;
  773. int dmode;
  774. {
  775.     int cpid, status;
  776.     struct stat statbuf;
  777.  
  778.     if (stat(dpath,&statbuf) == 0) {
  779.         errno = EEXIST;        /* Stat worked, so it already exists */
  780.         return -1;
  781.     }
  782.  
  783.     /* If stat fails for a reason other than non-existence, return error */
  784.     if (errno != ENOENT) return -1; 
  785.  
  786.     switch (cpid = fork()) {
  787.  
  788.     case -1:            /* Error in fork() */
  789.         return(-1);        /* Errno is set already */
  790.  
  791.     case 0:                /* Child process */
  792.         /*
  793.          * Cheap hack to set mode of new directory.  Since this
  794.          * child process is going away anyway, we zap its umask.
  795.          * FIXME, this won't suffice to set SUID, SGID, etc. on this
  796.          * directory.  Does anybody care?
  797.          */
  798.         status = umask(0);    /* Get current umask */
  799.         status = umask(status | (0777 & ~dmode)); /* Set for mkdir */
  800.         execl("/bin/mkdir", "mkdir", dpath, (char *)0);
  801.         _exit(2);        /* Can't exec /bin/mkdir */
  802.     
  803.     default:            /* Parent process */
  804.         while (cpid != wait(&status)) ;    /* Wait for kid to finish */
  805.     }
  806.  
  807.     if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) {
  808.         errno = EIO;        /* We don't know why, but */
  809.         return -1;        /* /bin/mkdir failed */
  810.     }
  811.  
  812.     return 0;
  813. }
  814. #endif /* MD != 2 */
  815. #endif /* MD */
  816.  
  817. /*
  818.  * Putsec writes the n characters of buf to receive file fout.
  819.  *  If not in binary mode, carriage returns, and all characters
  820.  *  starting with CPMEOF are discarded.
  821.  */
  822. putsec(buf, n)
  823. char *buf;
  824. register n;
  825. {
  826.     register char *p;
  827.  
  828.     if (n == 0)
  829.         return OK;
  830.     if (Thisbinary) {
  831.         for (p=buf; --n>=0; )
  832.             putc( *p++, fout);
  833.     }
  834.     else {
  835.         if (Eofseen)
  836.             return OK;
  837.         for (p=buf; --n>=0; ++p ) {
  838.             if ( *p == '\r')
  839.                 continue;
  840.             if (*p == CPMEOF) {
  841.                 Eofseen=TRUE; return OK;
  842.             }
  843.             putc(*p ,fout);
  844.         }
  845.     }
  846.     return OK;
  847. }
  848.  
  849. /*
  850.  * substr(string, token) searches for token in string s
  851.  * returns pointer to token within string if found, NULL otherwise
  852.  */
  853. char *
  854. substr(s, t)
  855. register char *s,*t;
  856. {
  857.     register char *ss,*tt;
  858.     /* search for first char of token */
  859.     for (ss=s; *s; s++)
  860.         if (*s == *t)
  861.             /* compare token with substring */
  862.             for (ss=s,tt=t; ;) {
  863.                 if (*tt == 0)
  864.                     return s;
  865.                 if (*ss++ != *tt++)
  866.                     break;
  867.             }
  868.     return NULL;
  869. }
  870.  
  871.  
  872. /*
  873.  * If called as rb use YMODEM protocol, etc.
  874.  */
  875. chkinvok(s)
  876. char *s;
  877. {
  878.     register char *p;
  879.  
  880.     p = s;
  881.     while (*p == '-')
  882.         s = ++p;
  883.     while (*p)
  884.         if (*p++ == '/')
  885.             s = p;
  886.     if (*s == 'v') {
  887.         Verbose=1; ++s;
  888.     }
  889.     Progname = s;
  890.     if (s[0]=='r' && s[1]=='z')
  891.         Batch = TRUE;
  892.     if (s[0]=='r' && s[1]=='c')
  893.         Crcflg = TRUE;
  894.     if (s[0]=='r' && s[1]=='b')
  895.         Batch = Nozmodem = TRUE;
  896. }
  897.  
  898. /*
  899.  * Totalitarian Communist pathname processing
  900.  */
  901. checkpath(name)
  902. char *name;
  903. {
  904.     if (Restricted) {
  905.         if (fopen(name, "r") != NULL) {
  906.             canit();
  907.             fprintf(stderr, "\r\nrz: %s exists\n", name);
  908.             bibi(-1);
  909.         }
  910.         /* restrict pathnames to current tree or uucppublic */
  911.         if ( substr(name, "../")
  912.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  913.             canit();
  914.             fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n");
  915.             bibi(-1);
  916.         }
  917.     }
  918. }
  919. /*
  920.  * Ack a ZFIN packet, let byegones be byegones
  921.  */
  922. void
  923. ackbibi()
  924. {
  925.     register n;
  926.  
  927.     vfile("ackbibi:");
  928.     Readnum = 1;
  929.     stohdr(0L);
  930.     for (n=3; --n>=0; ) {
  931.         purgeline();
  932.         zshhdr(4,ZFIN, Txhdr);
  933.         switch (readline(100)) {
  934.         case 'O':
  935.             readline(1);    /* Discard 2nd 'O' */
  936.             vfile("ackbibi complete");
  937.             return;
  938.         case RCDO:
  939.             return;
  940.         case TIMEOUT:
  941.         default:
  942.             break;
  943.         }
  944.     }
  945. }
  946.  
  947.  
  948. /*
  949.  * Initialize for Zmodem receive attempt, try to activate Zmodem sender
  950.  *  Handles ZSINIT frame
  951.  *  Return ZFILE if Zmodem filename received, -1 on error,
  952.  *   ZCOMPL if transaction finished,  else 0
  953.  */
  954. tryz()
  955. {
  956.     register c, n;
  957.     register cmdzack1flg;
  958.  
  959.     if (Nozmodem)        /* Check for "rb" program name */
  960.         return 0;
  961.  
  962.  
  963.     for (n=15; --n>=0; ) {
  964.         /* Set buffer length (0) and capability flags */
  965. #ifdef SEGMENTS
  966.         stohdr(SEGMENTS*1024L);
  967. #else
  968.         stohdr(0L);
  969. #endif
  970. #ifdef CANBREAK
  971.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK;
  972. #else
  973.         Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO;
  974. #endif
  975.         if (Zctlesc)
  976.             Txhdr[ZF0] |= TESCCTL;
  977.         Txhdr[ZF0] |= CANRLE;
  978.         Txhdr[ZF1] = CANVHDR;
  979.         /* tryzhdrtype may == ZRINIT */
  980.         zshhdr(4,tryzhdrtype, Txhdr);
  981.         if (tryzhdrtype == ZSKIP)    /* Don't skip too far */
  982.             tryzhdrtype = ZRINIT;    /* CAF 8-21-87 */
  983. again:
  984.         switch (zgethdr(Rxhdr)) {
  985.         case ZRQINIT:
  986.             if (Rxhdr[ZF3] & 0x80)
  987.                 Usevhdrs = 1;    /* we can var header */
  988.             continue;
  989.         case ZEOF:
  990.             continue;
  991.         case TIMEOUT:
  992.             continue;
  993.         case ZFILE:
  994.             zconv = Rxhdr[ZF0];
  995.             zmanag = Rxhdr[ZF1];
  996.             ztrans = Rxhdr[ZF2];
  997.             if (Rxhdr[ZF3] & ZCANVHDR)
  998.                 Usevhdrs = TRUE;
  999.             tryzhdrtype = ZRINIT;
  1000.             c = zrdata(secbuf, 1024);
  1001.             if (c == GOTCRCW)
  1002.                 return ZFILE;
  1003.             zshhdr(4,ZNAK, Txhdr);
  1004.             goto again;
  1005.         case ZSINIT:
  1006.             Zctlesc = TESCCTL & Rxhdr[ZF0];
  1007.             if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
  1008.                 stohdr(1L);
  1009.                 zshhdr(4,ZACK, Txhdr);
  1010.                 goto again;
  1011.             }
  1012.             zshhdr(4,ZNAK, Txhdr);
  1013.             goto again;
  1014.         case ZFREECNT:
  1015.             stohdr(getfree());
  1016.             zshhdr(4,ZACK, Txhdr);
  1017.             goto again;
  1018.         case ZCOMMAND:
  1019. #ifdef REGISTERED
  1020.             /* Enabling this code without registering is theft */
  1021.             if (Restricted) {
  1022.                 sprintf(endmsg, "ZCOMMAND Restricted.");
  1023.                 return ERROR;
  1024.             }
  1025. #endif
  1026.             cmdzack1flg = Rxhdr[ZF0];
  1027.             if (zrdata(secbuf, 1024) == GOTCRCW) {
  1028.                 void exec2();
  1029.  
  1030.                 if (cmdzack1flg & ZCACK1)
  1031.                     stohdr(0L);
  1032.                 else
  1033.                     stohdr((long)sys2(secbuf));
  1034.                 purgeline();    /* dump impatient questions */
  1035.                 do {
  1036.                     zshhdr(4,ZCOMPL, Txhdr);
  1037.                 }
  1038.                 while (++errors<20 && zgethdr(Rxhdr) != ZFIN);
  1039.                 ackbibi();
  1040.                 if (cmdzack1flg & ZCACK1)
  1041.                     exec2(secbuf);
  1042.                 return ZCOMPL;
  1043.             }
  1044.             zshhdr(4,ZNAK, Txhdr); goto again;
  1045.         case ZCOMPL:
  1046.             goto again;
  1047.         default:
  1048.             continue;
  1049.         case ZFIN:
  1050.             ackbibi(); return ZCOMPL;
  1051.         case ZCAN:
  1052.             return ERROR;
  1053.         }
  1054.     }
  1055.     return 0;
  1056. }
  1057.  
  1058. /*
  1059.  * Receive 1 or more files with ZMODEM protocol
  1060.  */
  1061. rzfiles()
  1062. {
  1063.     register c;
  1064.  
  1065.     for (;;) {
  1066.         switch (c = rzfile()) {
  1067.         case ZEOF:
  1068.         case ZSKIP:
  1069.             switch (tryz()) {
  1070.             case ZCOMPL:
  1071.                 return OK;
  1072.             default:
  1073.                 return ERROR;
  1074.             case ZFILE:
  1075.                 break;
  1076.             }
  1077.             continue;
  1078.         default:
  1079.             return c;
  1080.         case ERROR:
  1081.             return ERROR;
  1082.         }
  1083.     }
  1084.     /* NOTREACHED */
  1085. }
  1086.  
  1087. /*
  1088.  * Receive a file with ZMODEM protocol
  1089.  *  Assumes file name frame is in secbuf
  1090.  */
  1091. rzfile()
  1092. {
  1093.     register c, n;
  1094.  
  1095.     Eofseen=FALSE;
  1096.     n = 20; rxbytes = 0l;
  1097.  
  1098.     if (c = procheader(secbuf)) {
  1099.         return (tryzhdrtype = c);
  1100.     }
  1101.  
  1102.     for (;;) {
  1103. #ifdef SEGMENTS
  1104.         chinseg = 0;
  1105. #endif
  1106.         stohdr(rxbytes);
  1107.         zshhdr(4,ZRPOS, Txhdr);
  1108. nxthdr:
  1109.         switch (c = zgethdr(Rxhdr)) {
  1110.         default:
  1111.             vfile("rzfile: Wrong header %d", c);
  1112.             if ( --n < 0) {
  1113.                 sprintf(endmsg, "rzfile: Wrong header %d", c);
  1114.                 return ERROR;
  1115.             }
  1116.             continue;
  1117.         case ZCAN:
  1118.             sprintf(endmsg, "Sender CANcelled");
  1119.             return ERROR;
  1120.         case ZNAK:
  1121. #ifdef SEGMENTS
  1122.             putsec(secbuf, chinseg);
  1123.             chinseg = 0;
  1124. #endif
  1125.             if ( --n < 0) {
  1126.                 sprintf(endmsg, "rzfile: got ZNAK", c);
  1127.                 return ERROR;
  1128.             }
  1129.             continue;
  1130.         case TIMEOUT:
  1131. #ifdef SEGMENTS
  1132.             putsec(secbuf, chinseg);
  1133.             chinseg = 0;
  1134. #endif
  1135.             if ( --n < 0) {
  1136.                 sprintf(endmsg, "rzfile: TIMEOUT", c);
  1137.                 return ERROR;
  1138.             }
  1139.             continue;
  1140.         case ZFILE:
  1141.             zrdata(secbuf, 1024);
  1142.             continue;
  1143.         case ZEOF:
  1144. #ifdef SEGMENTS
  1145.             putsec(secbuf, chinseg);
  1146.             chinseg = 0;
  1147. #endif
  1148.             if (rclhdr(Rxhdr) != rxbytes) {
  1149.                 /*
  1150.                  * Ignore eof if it's at wrong place - force
  1151.                  *  a timeout because the eof might have gone
  1152.                  *  out before we sent our zrpos.
  1153.                  */
  1154.                 errors = 0;  goto nxthdr;
  1155.             }
  1156.             if (closeit()) {
  1157.                 tryzhdrtype = ZFERR;
  1158.                 vfile("rzfile: closeit returned <> 0");
  1159.                 sprintf(endmsg,"Error closing file");
  1160.                 return ERROR;
  1161.             }
  1162.             vfile("rzfile: normal EOF");
  1163.             return c;
  1164.         case ERROR:    /* Too much garbage in header search error */
  1165. #ifdef SEGMENTS
  1166.             putsec(secbuf, chinseg);
  1167.             chinseg = 0;
  1168. #endif
  1169.             if ( --n < 0) {
  1170.                 sprintf(endmsg, "Persistent CRC or other ERROR");
  1171.                 return ERROR;
  1172.             }
  1173.             zmputs(Attn);
  1174.             continue;
  1175.         case ZSKIP:
  1176. #ifdef SEGMENTS
  1177.             putsec(secbuf, chinseg);
  1178.             chinseg = 0;
  1179. #endif
  1180.             Modtime = 1;
  1181.             closeit();
  1182.             sprintf(endmsg, "Sender SKIPPED file");
  1183.             return c;
  1184.         case ZDATA:
  1185.             if (rclhdr(Rxhdr) != rxbytes) {
  1186.                 if ( --n < 0) {
  1187.                     sprintf(endmsg,"Data has bad addr");
  1188.                     return ERROR;
  1189.                 }
  1190. #ifdef SEGMENTS
  1191.                 putsec(secbuf, chinseg);
  1192.                 chinseg = 0;
  1193. #endif
  1194.                 zmputs(Attn);  continue;
  1195.             }
  1196. moredata:
  1197.             if (Verbose>1)
  1198.                 fprintf(stderr, "%7ld ZMODEM%s\n",
  1199.                   rxbytes, Crc32r?" CRC-32":"");
  1200. #ifdef SEGMENTS
  1201.             if (chinseg >= (1024 * SEGMENTS)) {
  1202.                 putsec(secbuf, chinseg);
  1203.                 chinseg = 0;
  1204.             }
  1205.             switch (c = zrdata(secbuf+chinseg, 1024))
  1206. #else
  1207.             switch (c = zrdata(secbuf, 1024))
  1208. #endif
  1209.             {
  1210.             case ZCAN:
  1211. #ifdef SEGMENTS
  1212.                 putsec(secbuf, chinseg);
  1213.                 chinseg = 0;
  1214. #endif
  1215.                 sprintf(endmsg, "Sender CANcelled");
  1216.                 return ERROR;
  1217.             case ERROR:    /* CRC error */
  1218. #ifdef SEGMENTS
  1219.                 putsec(secbuf, chinseg);
  1220.                 chinseg = 0;
  1221. #endif
  1222.                 if ( --n < 0) {
  1223.                     sprintf(endmsg, "Persistent CRC or other ERROR");
  1224.                     return ERROR;
  1225.                 }
  1226.                 zmputs(Attn);
  1227.                 continue;
  1228.             case TIMEOUT:
  1229. #ifdef SEGMENTS
  1230.                 putsec(secbuf, chinseg);
  1231.                 chinseg = 0;
  1232. #endif
  1233.                 if ( --n < 0) {
  1234.                     sprintf(endmsg, "TIMEOUT");
  1235.                     return ERROR;
  1236.                 }
  1237.                 continue;
  1238.             case GOTCRCW:
  1239.                 n = 20;
  1240. #ifdef SEGMENTS
  1241.                 chinseg += Rxcount;
  1242.                 putsec(secbuf, chinseg);
  1243.                 chinseg = 0;
  1244. #else
  1245.                 putsec(secbuf, Rxcount);
  1246. #endif
  1247.                 rxbytes += Rxcount;
  1248.                 stohdr(rxbytes);
  1249.                 sendline(XON);
  1250.                 zshhdr(4,ZACK, Txhdr);
  1251.                 goto nxthdr;
  1252.             case GOTCRCQ:
  1253.                 n = 20;
  1254. #ifdef SEGMENTS
  1255.                 chinseg += Rxcount;
  1256. #else
  1257.                 putsec(secbuf, Rxcount);
  1258. #endif
  1259.                 rxbytes += Rxcount;
  1260.                 stohdr(rxbytes);
  1261.                 zshhdr(4,ZACK, Txhdr);
  1262.                 goto moredata;
  1263.             case GOTCRCG:
  1264.                 n = 20;
  1265. #ifdef SEGMENTS
  1266.                 chinseg += Rxcount;
  1267. #else
  1268.                 putsec(secbuf, Rxcount);
  1269. #endif
  1270.                 rxbytes += Rxcount;
  1271.                 goto moredata;
  1272.             case GOTCRCE:
  1273.                 n = 20;
  1274. #ifdef SEGMENTS
  1275.                 chinseg += Rxcount;
  1276. #else
  1277.                 putsec(secbuf, Rxcount);
  1278. #endif
  1279.                 rxbytes += Rxcount;
  1280.                 goto nxthdr;
  1281.             }
  1282.         }
  1283.     }
  1284. }
  1285.  
  1286.  
  1287. /*
  1288.  * Close the receive dataset, return OK or ERROR
  1289.  */
  1290. closeit()
  1291. {
  1292.     time_t time();
  1293.  
  1294.     if (fout == stdout) {
  1295.         fflush(stdout);  fout = 0;  return OK;
  1296.     }
  1297.     if (fclose(fout)==ERROR) {
  1298.         fprintf(stderr, "File close ERROR\n");
  1299.         return ERROR;
  1300.     }
  1301.     if (Modtime) {
  1302.         timep[0] = time(NULL);
  1303.         timep[1] = Modtime;
  1304.         utime(Pathname, timep);
  1305.     }
  1306.     if (
  1307. #ifdef POSIX
  1308.     S_ISREG(Filemode)
  1309. #else
  1310.     (Filemode&S_IFMT) == S_IFREG
  1311. #endif
  1312.     )
  1313.         chmod(Pathname, (unsigned short)(07777 & Filemode));
  1314.     return OK;
  1315. }
  1316.  
  1317.  
  1318. /*
  1319.  * Strip leading ! if present, do shell escape. 
  1320.  */
  1321. sys2(s)
  1322. register char *s;
  1323. {
  1324.     if (*s == '!')
  1325.         ++s;
  1326.     return system(s);
  1327. }
  1328. /*
  1329.  * Strip leading ! if present, do exec.
  1330.  */
  1331. void
  1332. exec2(s)
  1333. register char *s;
  1334. {
  1335.     if (*s == '!')
  1336.         ++s;
  1337.     mode(0);
  1338.     execl("/bin/sh", "sh", "-c", s);
  1339. }
  1340.  
  1341. /* End of rz.c */
  1342.